/*
    读写锁 RwLock
    Mutex会对每次读写都进行加锁，但某些时候，我们需要大量的并发读，Mutex就无法满足需求了，此时就可以使用RwLock:
*/

#[cfg(test)]
mod rw_lock {
    use std::sync::RwLock;

    /*
    RwLock在使用上和Mutex区别不大，只有在多个读的情况下不阻塞程序，
        其他如读写、写读、写写情况下均会对后获取锁的操作进行阻塞。

    RwLock
        同时允许多个读，但最多只能有一个写
        读和写不能同时存在
        读可以使用read、try_read，写write、try_write, 在实际项目中，try_xxx会安全的多
    */
    #[test]
    fn main() {
        let lock = RwLock::new(5);
        // 同一时间允许多个读
        {
            let r1 = lock.read().unwrap();
            let r2 = lock.read().unwrap();
            assert_eq!(*r1, 5);
            assert_eq!(*r2, 5);
        } // 读锁在此处被drop

        // 同一时间只允许一个写
        {
            let mut w = lock.write().unwrap();
            *w += 1;
            assert_eq!(*w, 6);
            // 以下代码会阻塞发生死锁，因为读和写不允许同时存在
            // 写锁w直到该语句块结束才被释放，因此下面的读锁依然处于`w`的作用域中
            // let r1 = lock.read();
            // println!("{:?}",r1);
        } // 写锁在此处被drop
    }
}
/*

Mutex 还是 RwLock
首先简单性上Mutex完胜，因为使用RwLock你得操心几个问题：
    读和写不能同时发生，如果使用try_xxx解决，就必须做大量的错误处理和失败重试机制
    当读多写少时，写操作可能会因为一直无法获得锁导致连续多次失败(writer starvation)
    RwLock 其实是操作系统提供的，实现原理要比Mutex复杂的多，因此单就锁的性能而言，比不上原生实现的Mutex
再来简单总结下两者的使用场景：
    追求高并发读取时，使用RwLock，因为Mutex一次只允许一个线程去读取
    如果要保证写操作的成功性，使用Mutex
    不知道哪个合适，统一使用Mutex
需要注意的是，RwLock虽然看上去貌似提供了高并发读取的能力，但这个不能说明它的性能比Mutex高，
    事实上Mutex性能要好不少，后者唯一的问题也仅仅在于不能并发读取。

一个常见的、错误的使用RwLock的场景就是使用HashMap进行简单读写，因为HashMap的读和写都非常快，
    RwLock的复杂实现和相对低的性能反而会导致整体性能的降低，因此一般来说更适合使用Mutex。

总之，如果你要使用RwLock要确保满足以下两个条件：并发读，且需要对读到的资源进行"长时间"的操作
，HashMap也许满足了并发读的需求，但是往往并不能满足后者："长时间"的操作。

*/

// 用条件变量(Condvar)控制线程的同步(来解决资源访问顺序的问题)

#[cfg(test)]
mod condvar {
    use std::sync::{Arc, Condvar, Mutex};
    use std::thread::{sleep, spawn};
    use std::time::Duration;

    #[test]
    fn main() {
        let flag = Arc::new(Mutex::new(false));
        let cond = Arc::new(Condvar::new()); //
        let cflag = flag.clone();
        let ccond = cond.clone();

        // 为了创建一个新线程，需要调用 thread::spawn 函数并传递一个闭包

        let hdl = spawn(move || {
            let mut lock = cflag.lock().unwrap();
            let mut counter = 0;
            // cflag 加锁 循环 3次
            while counter < 3 {
                while !*lock {
                    println!("lock wait :{}", !*lock);
                    // wait方法会接收一个MutexGuard<'a, T>，且它会自动地暂时释放这个锁，使其他线程可以拿到锁并进行数据更新。
                    // 同时当前线程在此处会被阻塞，直到被其他地方notify后，它会将原本的MutexGuard<'a, T>还给我们，即重新获取到了锁，同时唤醒了此线程。
                    lock = ccond.wait(lock).unwrap();
                    println!("wait back :{}", counter);
                }

                *lock = false;

                counter += 1;
                println!("lock inner:{}", counter);
            }
        });

        let mut counter = 0;
        loop {
            sleep(Duration::from_millis(100));
            *flag.lock().unwrap() = true;
            counter += 1;
            if counter > 3 {
                break;
            }
            println!("outside counter: {}", counter);
            cond.notify_one(); // 通知 wait
        }
        hdl.join().unwrap();
        println!("{:?}", flag);
    }
}
